#include <stdexcept>
#include <boost/bind.hpp>
#include "ThreadPool.h"

using namespace std;
//using namespace boost;
//using namespace boost::asio;
//using boost::asio::io_service::work;

namespace tsc
{
namespace common
{

//------------------------------------------------------------------------------
//
// Singleton instance
//
//------------------------------------------------------------------------------
ThreadPool* ThreadPool::mpInstance = 0;

ThreadPool* ThreadPool::instance()
{
  if (!mpInstance)
  {
    mpInstance = new ThreadPool();
  }
  return mpInstance;
} // ThreadPool::instance

//------------------------------------------------------------------------------
//
// Constructor
//
//------------------------------------------------------------------------------

ThreadPool::ThreadPool()
{

} // ThreadPool::ThreadPool

//------------------------------------------------------------------------------
//
// Destructor
//
//------------------------------------------------------------------------------
ThreadPool::~ThreadPool()
{

} // ThreadPool::~ThreadPool

//------------------------------------------------------------------------------
//
// Create a new io_service object and add to the pool
//
//------------------------------------------------------------------------------
boost::asio::io_service* ThreadPool::add()
{
  boost::asio::io_service*       pService   = new boost::asio::io_service;
  boost::asio::io_service::work* pWork      = new boost::asio::io_service::work(*pService);
  boost::asio::signal_set*       pSignalSet = new boost::asio::signal_set(*pService);

  shpIoService_t shpService(pService);
  shpWork_t      shpWork(pWork);
  shpSignalSet_t shpSignalSet(pSignalSet);

  pSignalSet->async_wait(boost::bind(&boost::asio::io_service::stop,
                                     pService));

  mIoServicePtrVec.push_back(shpService);
  mWorkPtrVec.push_back(shpWork);
  mSignalSetPtrVec.push_back(shpSignalSet);

  return pService;
} // ThreadPool::add

//------------------------------------------------------------------------------
//
// Get pool size
//
//------------------------------------------------------------------------------
u_int8_t ThreadPool::getPoolSize()
{
  return mIoServicePtrVec.size();
} // ThreadPool::getPoolSize

//------------------------------------------------------------------------------
//
// Get io_service form the pool
//
//------------------------------------------------------------------------------
boost::asio::io_service* ThreadPool::getIoService(u_int8_t index)
{
  return mIoServicePtrVec[index].get();
} // ThreadPool::getIoService

//------------------------------------------------------------------------------
//
// start all io_services
//
//------------------------------------------------------------------------------
void ThreadPool::startAll()
{
  vector<shpThread_t>      threadPtrVec;
  boost::thread*           pThread;
  boost::asio::io_service* pIoService;

  for (size_t i=0; i<mIoServicePtrVec.size(); ++i)
  {
    pIoService = mIoServicePtrVec[i].get();
    pThread    = new boost::thread(boost::bind(&boost::asio::io_service::run,
                                               pIoService));
    shpThread_t shpThread(pThread);
    threadPtrVec.push_back(shpThread);
  }

  for (size_t i=0; i<threadPtrVec.size(); ++i)
  {
    threadPtrVec[i]->join();
  }
} // ThreadPool::startAll

//------------------------------------------------------------------------------
//
// stop all io_services
//
//------------------------------------------------------------------------------
void ThreadPool::stopAll()
{
  for (size_t i = 0; i < mIoServicePtrVec.size(); ++i)
  {
    mIoServicePtrVec[i]->stop();
  }
} // ThreadPool::stopAll

//------------------------------------------------------------------------------
//
// add signal to all services
//
//------------------------------------------------------------------------------
void ThreadPool::addSignal(int signal_number)
{
  boost::asio::signal_set* pSignalSet;

  for (size_t i=0; i<mSignalSetPtrVec.size(); ++i)
  {
    pSignalSet = mSignalSetPtrVec[i].get();
    pSignalSet->add(signal_number);
  }
} // ThreadPool::addSignal

//------------------------------------------------------------------------------
//
// notify fork event to io_services
//
//------------------------------------------------------------------------------
void ThreadPool::notifyFork(boost::asio::io_service::fork_event event)
{
  for (size_t i = 0; i < mIoServicePtrVec.size(); ++i)
  {
    mIoServicePtrVec[i].get()->notify_fork(event);
  }
} // ThreadPool::notifyFork

} // namespace common
} // namespace tsc
